ASM 汇编语言 13

  • Created on 2014-11

教材:《汇编语言》(第二版)王爽 著 清华大学出版社

章十六、直接定址表

16.1 描述了单元长度的标号——数据标号

(1)以下程序中,code、a、b、start、s(后面带冒号“:”)
都是地址标号,仅表示内存单元地址

assume cs:code
code segment
a:     db 1, 2, 3, 4, 5, 6, 7, 8
b:     dw 0
start:
     mov si, offset a
     mov bx, offset b
    
     mov cx, 8
c0:
     mov al, cs:[si]
     mov ah, 0
     add cs:[bx], ax
     inc si
     loop c0

     mov ax, 4c00h
     int 21h

code ends
end start


(2)另一种——数据标号
不但表示内存单元地址,还表示其长度
无论是byte/word/dword。
如以下程序中的a、b标号,后面没跟“:”冒号

assume cs:code
code segment
a     db 1, 2, 3, 4, 5, 6, 7, 8
b     dw 0
start:
     mov si, 0
     mov cx, 8
c0:
     mov al, cs:[si]
     mov ah, 0
     add b, ax
     inc si
     loop c0

     mov ax, 4c00h
     int 21h

code ends
end start

以上a、b这种标号的使用示例如下:
mov ax, b     =     mov ax, cs:[8]
mov b, 2      =     mov word ptr cs:[8]
inc b            =     inc word ptr cs:[8]

mov al, a[si]     =     mov al, cs:0[si]
mov al, a[3]      =     mov al, cs:0[3]

这句会引发编译错误!—— mov al, b 或 mov b, al



检测点1.6
将a处的8个数据累加,结果存储到b处的双字中,补全程序。

assume cs:code
code segment
a     dw 0fff1h, 0fff2h, 0fff3h, 0fff4h, 0fff5h, 0fff6h, 0fff7h, 0fff8h
b     dd 0
start:
     mov si, 0
     mov cx, 8
c0:
     mov ax, a:[si]
     ;下一句可简化为 add word ptr b, ax
     add word ptr b[0], ax     
     adc word ptr b[2], 0

     inc si
     inc si
     loop c0

     mov ax, 4c00h
     int 21h

code ends
end start



16.2 在其它段中,使用数据标号

地址标号(带冒号后缀):
只能在代码段中使用,不能在其它段使用。

     >>assume的作用:
若想在代码段中直接用数据标号访问数据,
要用伪指令assume
将标号所在的段和段寄存器联系起来。
否则编译时,无法确定标号的段地址在哪一个寄存器中。
(下文实例详细说明)

此种联系是编译器需要的,
但段寄存器实际上不一定会真的存放该段的地址。
还要用指令对段寄存器进行设置。

(例)
assume cs:code, ds:data
data segment
     a db 1, 2, 3, 4, 5, 6, 7, 8
     b dw 0
data ends
code segment
start:
     mov ax, data
     mov ds, ax
    
     mov si, 0
     mov cx, 8
c0:
     mov al, a[si]
     mov ah, 0
     add b, ax
    
     inc si
     loop c0
    
     mov ax, 4c00h
     int 21h
code ends
end start

已有assume ..., ds:data
但是还要
mov ax, data
mov ds, ax
然后以下就是assume ds:data编译实际的影响
mov al, a[si] 编译为 mov al, [si+0]
add b, ax 编译为 add [8], ax

删除以上源程序中assume ..., ds:data中的ds:data后,
用masm编译时,会产生错误,报错信息如下:
t.asm(14)是这一句:mov al, a[si]
编译程序不知道标号在哪里了!

当然可将以下语句移到 code segment(段)中,
就可顺利编译,但原理必须明白!:
     a db 1, 2, 3, 4, 5, 6, 7, 8
     b dw 0


标号可以作为数据定义
data segment
     a db 1, 2, 3, 4, 5, 6, 7, 8
     b dw 0
     c dw a, b     ;相当于 c dw offset a, offset b
     d dd a, b     ;相当于 d dw offset a, seg a, offset b, seg b
data ends



16.3 直接定址表

根据我的理解,就像数组
实例如下:

assume cs:code
code segment
start:
     mov ax, 2bh
     call showbyte
    
     mov ax, 4c00h
     int 21h
    
showbyte:
     jmp short show
     table db '0123456890ABCDEF'     ;字符表
show:
     push bx
     push cx
     push es
    
     mov ah, al;
     mov cl, 4
     shr ah, cl     ;ah得到原al的高4位
     and al, 00001111b     ;al得到原al的低4位
    
     mov bl, ah
     mov bh, 0
     mov ah, table[bx]
    
     mov bx, 0b800h
     mov es, bx
     mov es:[160 * 12 + 40 * 2], ah
    
     mov bl, al
     mov bh, 0
     mov al, table[bx]
    
     mov es:[160 * 12 + 40 * 2 + 2], al
    
     pop es
     pop cx
     pop bx
     ret
code ends
end start

在以上程序中,我们在(1byte大小的)数值 0~15
字符 “0” ~ “F”(16进制表示法)之间建立的映射关系为:
数值N为table表中的偏移,可以找到对应字符


利用表,建立两个数据集之间的一种映射关系,
根据给出的数据,得到另一数据集对应的数据,目的是:
(1)为了算法的清晰和简洁;
(2)加快运算速度;
(3)使程序易于扩充。


编程:写一个子程序,计算sin(x),
          x属于{0, 30, 60, 90, 120, 150, 180}集合(单位:度)。
          如,sin(30)结果显示为“0.5”。

assume cs:code
code segment
start:
     mov al, 255
     call showsin
    
     mov ax, 4c00h
     int 21h
    
showsin:
     jmp short show
     ;字符串偏移地址表
     table dw s0, s30, s60, s90, s120, s150, s180

     s0          db '0', 0
     s30          db '0.5', 0
     s60          db '0.866', 0
     s90          db '1', 0
     s120     db '0,866', 0
     s150     db '0.5', 0
     s180     db '0', 0
show:
     push bx
     push es
     push si
    
     mov bx, 0b800h
     mov es, bx
    
     ;以下用“角度值/30”作为table的偏移,
     ;取得对应字符串的偏移地址,置于bx中
     mov ah, 0
     mov bl, 30
     div bl
     mov bl, al     ;高字节ah存余,低字节al存商
     mov bh, 0
     add bx, bx     ;偏移地址为word型,以bx * 2以对应地址
     mov bx, table[bx]
    
     ;以下显示sin(x)对应的字符串
     mov si, 160 * 12 + 40 * 2
shows:
     mov ah, cs:[bx]
     cmp ah, 0
     je ok
    
     mov es:[si], ah
     inc bx
     inc si
     inc si
     jmp short shows
    
ok:    
     pop si
     pop es
     pop bx
     ret
code ends
end start

角度值没有检测,可能超范围(0~180),
真实编程需要检测。


16.4 程序入口地址的直接定址表

可以在直接定址表存储子程序地址
从而方便地实现不同子程序的调用

编程:
实现一个子程序setscreen,为显示输出提供如下功能:
(1)清屏;
(2)设置前景色;
(3)设置背景色;
(4)向上滚动一行。

入口参数说明如下:
a. 用ah寄存器传递功能号:
     0表示以上功能(1)清屏,
     1表示(2),2表示(3),3表示(4)。
b. 对于功能(2)、(3),用al传递颜色值,
     (al)属于{0, 1, 2, 3, 4, 5, 6, 7}范围。

下面是实现思路:
(1)清屏:将屏幕字符设置为空格;
(2)前景色:设置屏幕字符属性字节的第0、1、2位;
(3)背景色:设置屏幕字符属性字节的第4、5、6位;
(4)向上滚一行:依次将第n + 1行复制到第n行处,最后一行置空。

源代码:
assume cs:code
code segment
start:
     mov ah, 3
     mov al, 3
     call setscreen
    
     mov ax, 4c00h
     int 21h
    
setscreen:
     jmp short set
     table dw sub1, sub2, sub3, sub4
set:
     push bx
    
     cmp ah, 3
     ja sret

     mov bl, ah
     mov bh, 0
     add bx, bx

    
     call word ptr table[bx]     ;调用对应的功能子程序
sret:
     pop bx
     ret
    
    
sub1:
     push bx
     push cx
     push es
    
     mov bx, 0b800h
     mov es, bx
     mov bx, 0
    
     mov cx, 2000
sub1c0:
     mov byte ptr es:[bx], ' '
     inc bx
     inc bx
     loop sub1c0
    
     pop es
     pop cx
     pop bx
     ret

    
sub2:
     push bx
     push cx
     push es
    
     mov bx, 0b800h
     mov es, bx
     mov bx, 1
    
     mov cx, 2000
sub2c0:
     and byte ptr es:[bx], 11111000b
     or es:[bx], al
     inc bx
     inc bx
     loop sub2c0
    
     pop es
     pop cx
     pop bx
     ret
    
    
sub3:
     push bx
     push cx
     push es
    
     mov cl, 4
     shl al, cl

    
     mov bx, 0b800h
     mov es, bx
     mov bx, 1
    
     mov cx, 2000
sub3c0:
     and byte ptr es:[bx], 10001111b
     or es:[bx], al

     inc bx
     inc bx
     loop sub3c0
    
     pop es
     pop cx
     pop bx
     ret
    
    
sub4:
     push cx
     push ds
     push si
     push es
     push di
    
     mov si, 0b800h
     mov ds, si
     mov es, si
    
     mov si, 160
     mov di, 0
     cld
    
     mov cx, 24
sub4c0:
     push cx
    
     mov cx, 160
     rep movsb

    
     pop cx
     loop sub4c0
    
     mov cx, 80
     mov si, 0
sub4c1:
     mov byte ptr es:[160 * 24 + si], ' '
     inc si
     inc si
     loop sub4c1
    
     pop di
     pop es
     pop si
     pop ds
     pop cx
     ret
    
code ends
end start



实验16 编写包含多个功能子程序的中断例程
功能:安装一个新的int 7ch 中断例程,
          为显示输出提供如下功能子程序:
(1)清屏;
(2)设置前景色;
(3)设置背景色;
(4)向上滚动一行。

          入口参数说明如下:
(1)用ah寄存器传递功能号:0表示清屏,
     1表示设置前景色,2表示设置背景色,
     3表示向上滚动一行;
(2)对于2、3号功能,用al传送颜色值,
     (al)属于{0,1,2,3,4,5,6,7}

代码:
;安装int 7ch中断例程
assume cs:code
code segment
start:
     ;install int 7ch
     push cs
     pop ds
     mov si, offset set_screen
    
     mov ax, 0
     mov es, ax
     mov di, 200h
    
     mov cx, offset s_s_end - offset set_screen
     cld
     rep movsb
    
     cli
     mov word ptr es:[7ch * 4], 200h
     mov word ptr es:[7ch * 4 + 2], 0
     sti

     mov ax, 4c00h
     int 21h
    
set_screen:
     jmp short sel_sub
table:
     dw offset sub0 - offset set_screen + 200h, offset sub1 - offset set_screen + 200h, offset sub2 - offset set_screen + 200h, offset sub3 - offset set_screen + 200h

sel_sub:
     cmp ah, 3
     ja end_sub
    
     push bx
     push es
    
     mov bx, 0
     mov es, bx
    
     mov bh, 0
     mov bl, ah
     add bx, bx     ;千万记得将bx加倍!
                    ;以对应table中储存子程序入口地址的偏移量

cal_add:
     call word ptr es:[bx][offset table - offset set_screen + 200h]
    
end_sub:
     pop es
     pop bx
     iret

sub0:
     push bx
     push cx
     push es
    
     mov bx, 0b800h
     mov es, bx
     mov bx, 0
    
     mov cx, 25 * 80
sub0c0:
     mov byte ptr es:[bx], ' '
     inc bx
     inc bx
     loop sub0c0
    
     pop es
     pop cx
     pop bx
     ret

sub1:
     cmp al, 7     ;对于输入的(al)颜色参数,
     ja sub1_ok     ;第一种处理方式,检测范围


     push bx
     push cx
     push es
    
     mov bx, 0b800h
     mov es, bx
     mov bx, 1
    
     mov cx, 25 * 80
sub1c0:
     and byte ptr es:[bx], 11111000b
     or byte ptr es:[bx], al

     inc bx
     inc bx
     loop sub1c0
    
     pop es
     pop cx
     pop bx
sub1_ok:
     ret

sub2:
     push bx
     push cx
     push es
    
     and al, 00000111b     ;对于输入的(al)颜色参数,
                              ;第二种处理方式,清除超过范围的部分

     mov cl, 4
     shl al, cl

    
     mov bx, 0b800h
     mov es, bx
     mov bx, 1
    
     mov cx, 25 * 80
sub2c0:
     and byte ptr es:[bx], 10001111b
     or byte ptr es:[bx], al

     inc bx
     inc bx
     loop sub2c0
    
     pop es
     pop cx
     pop bx
     ret

sub3:
     push bx
     push cx
     push ds
     push es
    
     mov bx, 0b80ah     ;之前错写成0b8a0h了!仔细领悟一下!
          ;之所以偏移的行数过多,因为ds:[bx] = ds * 16 + bx!

     mov ds, bx
     mov bx, 0b800h
     mov es, bx
     mov bx, 0
    
     mov cx, 24 * 80
sub3c0:
     push ds:[bx]
     pop es:[bx]

     inc bx
     inc bx
     loop sub3c0
    
     mov cx, 80
sub3c1:
     mov byte ptr es:[bx], ' '
     inc bx
     inc bx
     loop sub3c1
    
     pop es
     pop ds
     pop cx
     pop bx
     ret
    
s_s_end:
     nop
code ends
end start



;测试程序
assume cs:code
code segment
start:
     ;test
     mov ah, 0
     ;mov al, 3
     int 7ch

     mov ax, 4c00h
     int 21h
code ends
end start



;sub3的更优写法:
sub3:
     push cx
     push di
     push ds
     push es
     push si
    
     mov si, 0b800h
     mov ds, si
     mov es, si

    
     mov si, 160
     mov di, 0

    
     mov cx, 24 * 160
     cli
     rep movsb

    
     mov cx, 80
sub3c1:
     mov byte ptr es:[di], ' '
     inc di
     inc di
     loop sub3c1
    
     pop si
     pop es
     pop ds
     pop di
     pop cx
     ret
0%